package com.zhentao.service.impl;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhentao.mapper.TaskinfoLogsMapper;
import com.zhentao.model.common.schedule.dto.TaskDto;
import com.zhentao.model.common.schedule.pojo.Taskinfo;
import com.zhentao.model.common.schedule.pojo.TaskinfoLogs;
import com.zhentao.service.TaskinfoService;
import com.zhentao.mapper.TaskinfoMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Calendar;
import java.util.Date;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
* @author EWEWW
* @description 针对表【taskinfo】的数据库操作Service实现
* @createDate 2024-06-21 11:43:17
*/
@Service
public class TaskinfoServiceImpl extends ServiceImpl<TaskinfoMapper, Taskinfo>
    implements TaskinfoService{
    @Autowired
    private TaskinfoMapper taskinfoMapper;
    @Autowired
    private TaskinfoLogsMapper taskinfoLogsMapper;
    @Resource
    private RedisTemplate redisTemplate;


    @Override
    public long addTask(TaskDto task) {
        //1.添加任务到数据库
        Long taskId = addTaskToDB(task);
        //2.添加数据到redis
        task.setTaskId(taskId);
       addTaskToRedis(task);
        //3.返回taskId
        return taskId;
    }

    @Override
    public TaskDto poll(int type, int priority) {
        String key = type + "_" + priority;
        String currentTopic = "submit:currentTopic:" + key;
        //rightPop之后数据就从list里面消失了
        String task = (String) redisTemplate.opsForList().rightPop(currentTopic);

        if(StringUtils.isNotBlank(task)){
            //把从redis里面的数据获取到，解析成对象
            TaskDto taskDto = JSON.parseObject(task, TaskDto.class);
            System.out.println("返回数据："+taskDto);
            //修改数据库信息，任务被执行
            return taskDto;
        }
        return null;
    }

    public void addTaskToRedis(TaskDto taskDto) {
        //设置redis的key
        String key = taskDto.getTaskType() + "_" + taskDto.getPriority();
        String currentTopic = "submit:currentTopic:" + key;
        String futureTopic = "submit:future:" + key;

        //获取5分钟之后的毫秒值
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE, 5);
        Long nextTime = calendar.getTimeInMillis();

        /**
         *   如果任务的执行时间<=当前的时候,直接存入redis的list,去实时消费
         *   如果>=当前的时间，并且在5分钟之内的任务，存入zSet里面
         *   大于5分钟的任务先保存在数据库里面，然后再同步到redis的zSet里面
         */
//        System.currentTimeMillis();
        System.out.println(taskDto.getExecuteTime() + ";" + System.currentTimeMillis() + ";" + nextTime);

        if (taskDto.getExecuteTime() <= System.currentTimeMillis()) {
            //直接存redis的list里面
            System.out.println(currentTopic + "存入数据到list" + taskDto);
            redisTemplate.opsForList().leftPush(currentTopic, JSON.toJSONString(taskDto));
        } else if (taskDto.getExecuteTime() <= nextTime) {
            //存入zSet里面
            System.out.println(futureTopic + "存入数据到zSet" + taskDto);
           redisTemplate.opsForZSet().add(futureTopic, JSON.toJSONString(taskDto),taskDto.getExecuteTime());
        }
    }

    public Long addTaskToDB(TaskDto dto){
        //保存到任务表
        Taskinfo taskinfo = new Taskinfo();
        BeanUtils.copyProperties(dto, taskinfo);
        taskinfo.setExecuteTime(new Date(dto.getExecuteTime()));
        taskinfoMapper.insert(taskinfo);

        //保存任务日志
        TaskinfoLogs taskinfoLogs = new TaskinfoLogs();
        BeanUtils.copyProperties(taskinfo,taskinfoLogs);
        taskinfoLogs.setVersion(1);
        taskinfoLogs.setStatus(0);
        taskinfoLogsMapper.insert(taskinfoLogs);

        return taskinfo.getTaskId();
    }

    @Scheduled(cron = "0 */1 * * * ?")
    public void zSetToList(){
        System.out.println("执行了定时任务，从zSer->list"+System.currentTimeMillis());
       if(teyLock("submit:zSetToList", 5L)){
           //1.获取当前需要消费的数据
           ZSetOperations zSetOperations = redisTemplate.opsForZSet();
           Set<String> tacks = zSetOperations.rangeByScore("submit:future:1_1", 0, System.currentTimeMillis());
           //2.将这些数据放到list里面
           for(String task: tacks){
               redisTemplate.opsForList().leftPush("submit:currentTopic:1_1", task);
               System.out.println("成功的将当前需要执行的任务数据刷新到list里面");
               //3.把放入list的数据从zSet里面删除
               System.out.println("把放入list的数据从zSet里面删除");
               zSetOperations.remove("submit:future:1_1", task);
           }

           redisTemplate.executePipelined(new RedisCallback<Object>() {
               @Override
               public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                   //2.将这些数据放到list里面
                   for (String task : tacks) {
                       redisTemplate.opsForList().leftPush("submit:currentTopic:1_1", task);
                       System.out.println("成功的将当前需要执行的任务数据刷新到list里面");
                       //3.把放入list的数据从zSet里面删除
                       System.out.println("把放入list的数据从zSet里面删除");
                       zSetOperations.remove("submit:future:1_1", task);
                   }
                   return null;
               }
           });
       }
    }

    /**
     * 锁的名字
     */
   public Boolean teyLock(String name,Long expire){
       String token = null;
       try{
           name = name + "_lock";
           token = UUID.randomUUID().toString();//value值
           // 设置5s过期
           Boolean result = redisTemplate.opsForValue().setIfAbsent(name,token,expire, TimeUnit.SECONDS);
           if(result){
               //加锁成功
               return true;
           }else {
               return false;
           }
       }finally {
           //释放锁 确保谁加锁谁释放
           if(token.equals(redisTemplate.opsForValue().get(name))){
               redisTemplate.delete(name);
           }
       }
   }
}




